package org.apache.ibatis.common.reflection;

import org.apache.ibatis.common.reflection.factory.ObjectFactory;
import org.apache.ibatis.common.reflection.property.PropertyTokenizer;
import org.apache.ibatis.common.reflection.wrapper.*;

import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @Name MetaObject
 * @Description: “元” 对象
 * 描述信息：“元” 作为词头，有 “涵盖”、“超越”、“变换” 的含义。“元” 对象是一个针对普通对象（Object）的反射包装类，涵盖了对象中更多的细节信息和功能。
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-04-28 13:48:19
 **/
public class MetaObject {
    private final Object object;
    private final ObjectFactory objectFactory;
    private final ObjectWrapper objectWrapper;
    private final ObjectWrapperFactory objectWrapperFactory;
    private final ReflectorFactory reflectorFactory;

    private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        this.object = object;
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
        if (object instanceof ObjectWrapper)
            this.objectWrapper = (ObjectWrapper) object;
        else if (objectWrapperFactory.hasObjectWrapperFor(object))
            this.objectWrapper = objectWrapperFactory.getObjectWrapperFor(this, object);
        else if (object instanceof Map)
            this.objectWrapper = new MapWrapper(this, (Map) object);
        else if (object instanceof Collection)
            this.objectWrapper = new CollectionWrapper((Collection) object);
        else
            this.objectWrapper = new BeanWrapper(this, object);
    }

    public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        if (object == null) return SystemMetaObject.NULL_META_OBJECT;
        else return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }

    public Object getObject() {
        return object;
    }

    public ObjectFactory getObjectFactory() {
        return objectFactory;
    }

    public ObjectWrapper getObjectWrapper() {
        return objectWrapper;
    }

    public ObjectWrapperFactory getObjectWrapperFactory() {
        return objectWrapperFactory;
    }

    public ReflectorFactory getReflectorFactory() {
        return reflectorFactory;
    }

    public String findProperty(String propName, boolean useCamelCaseMapping) {
        return objectWrapper.findProperty(propName, useCamelCaseMapping);
    }

    public String[] getGetterNames() {
        return objectWrapper.getGetterNames();
    }

    public String[] getSetterNames() {
        return objectWrapper.getSetterNames();
    }

    public Class<?> getGetterType(String name) {
        return objectWrapper.getGetterType(name);
    }

    public Class<?> getSetterType(String name) {
        return objectWrapper.getSetterType(name);
    }

    public boolean hasGetter(String name) {
        return objectWrapper.hasGetter(name);
    }

    public boolean hasSetter(String name) {
        return objectWrapper.hasSetter(name);
    }

    public Object getValue(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        // 如果还有下层属性
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) return null;
                // 循环解析
            else return metaValue.getValue(prop.getChildren());
        } else return objectWrapper.get(prop);
    }

    public void setValue(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        // 如果还有下层属性
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                // 如果 value 不存在，则不再实例化下层属性
                if (value == null) return;
            } else metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
            metaValue.setValue(prop.getChildren(), value);
        } else objectWrapper.set(prop, value);
    }

    public MetaObject metaObjectForProperty(String name) {
        Object value = getValue(name);
        return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
    }

    public boolean isCollection() {
        return objectWrapper.isCollection();
    }

    public void add(Object element) {
        objectWrapper.add(element);
    }

    public <E> void addAll(List<E> list) {
        objectWrapper.addAll(list);
    }

}

